
import struct
import time
import re

import logging
import oss2
import requests
import json
import argparse
import os


class MyOSS:
    '''
    阿里云OSS API的简易封装
    '''

    def __init__(self, access_id, access_secret, endpoint, bucketname):
        self.auth = oss2.Auth(access_id, access_secret)
        self.bucket = oss2.Bucket(self.auth, endpoint, bucketname)

    def exists(self, tag, name):
        '''
        文件存在性验证

        参数列表
            tag   (str)  备份类别名称
            name  (str)  备份名
        '''
        return self.bucket.object_exists('/'.join((tag, name)))

    def put(self, tag, name, file):
        '''
        上传数据

        参数列表
            tag   (str)  备份类别名称
            name  (str)  备份名
            file  (fp)   文件对象
        '''
        if self.exists(tag, name):
            raise ValueError('File name exists.')
        fqsp = '/'.join((tag, name))
        return self.bucket.put_object(fqsp, file)

    def get(self, tag, name, file):
        '''
        下载数据

        参数列表
            tag   (str)  备份类别名称
            name  (str)  备份名
            file  (fp)   文件对象
        '''
        fqsp = '/'.join((tag, name))
        if not hasattr(file, 'write'):
            raise ValueError('Unsuitable file type')
        istream = self.bucket.get_object(fqsp)
        file.write(istream.read())

    def delete(self, tag, name):
        '''
        删除数据

        参数列表
            tag   (str)  备份类别名称
            name  (str)  备份名
        '''
        fqsp = '/'.join((tag, name))
        return self.bucket.delete_object(fqsp)

    def ls(self, tag=None):
        '''
        枚举数据

        参数列表
            tag   (str)  备份类别名称
        '''
        if tag == None:
            dirs = list()
            for obj in oss2.ObjectIterator(self.bucket, delimiter='/'):
                if obj.is_prefix():
                    dirs.append(obj.key.split('/')[0])
            return dirs
        else:
            return oss2.ObjectIterator(self.bucket, prefix=tag + '/')


class metadata:
    def __init__(self):
        self.fmt = 'Q64s512s'
        '''
        Q-时间戳;64s-版权信息;512s-描述信息
        '''

    def pack(self, **kwargs):
        '''
        keywords:
            timestamp  int    时间戳     默认round(time.time())
            copyright  bytes  版权信息   默认b''
            describe   bytes  描述信息   默认b''
        '''
        return struct.pack(self.fmt,
                           kwargs.get('timestamp') or round(time.time()),
                           kwargs.get('copyright') or b'',
                           kwargs.get('describe') or b'')

    def unpack(self, buffer):
        keys = ['timestamp', 'copyright', 'describe']
        info = struct.unpack(self.fmt, buffer)
        return {key: value for key, value in zip(keys, info)}


def bingimg_gen(index, count):
    url = 'http://cn.bing.com/HPImageArchive.aspx'
    # 准备Get请求的参数
    param = {'format': 'js'}
    param['idx'] = '0' if type(index) != int else str(index)
    param['n'] = '1' if type(count) != int else str(count)
    # 请求Json文件
    ret = requests.get(url, params=param)
    assert ret.status_code == requests.codes.ok
    jsondat = ret.json()['images']
    # 准备分割copyright项的正则表达式
    re_copyright = re.compile(r'\(©.*\)')
    re_describe = re.compile(r'[^\(©.*\)]+')
    for imgdat in jsondat:
        imgurl = 'http://cn.bing.com' + imgdat['url']
        timestr = imgdat['fullstartdate']
        mixedstr = imgdat['copyright']
        description = re_describe.search(mixedstr).group()
        copyrights = re_copyright.search(mixedstr).group()[1:-2]
        img = requests.get(imgurl)
        struct_time = time.strptime(timestr, '%Y%m%d%H%M')
        timestamp = round(time.mktime(struct_time))
        yield {'describe': description, 'copyright': copyrights, 'date': timestr, 'img': img.content, 'timestamp': timestamp}


def OSSLoad(cfgfilepath):
    filename = cfgfilepath
    if cfgfilepath == None:
        if not os.path.exists('./default.json'):
            # 什么配置文件都没有
            raise RuntimeError('缺少OSS配置文件')
        else:
            filename = './default.json'
    with open(filename, 'r') as jsonfile:
        jsonobj = json.load(jsonfile)
        return MyOSS(jsonobj['access_id'],
                     jsonobj['access_secret'],
                     jsonobj['endpoint'],
                     jsonobj['bucket'])


def argparser():
    '''
    配置预设的命令行参数解析器，并返回该解析器
    argparser()->parser

    参数列表：
        None

    返回值：
        parser (argparse.ArgumentParser) 命令行参数解析器

    '''
    myparser = argparse.ArgumentParser(description='必应每日图片抓取脚本')
    myparser.add_argument('-n', action="store", dest='num', help="抓取天数")
    myparser.add_argument('-l', action="store", dest="logfile", help="日志保存地址")
    myparser.add_argument('-c', action="store", dest="cfg",
                          help="OSS授权配置文件（默认为脚本所在目录中的default.json）")
    myparser.add_argument('-v', '--version', action='version',
                          version='%(prog)s {version}'.format(version=1))
    return myparser


if __name__ == "__main__":
    PARAMS = argparser().parse_args()
    logging.basicConfig(filename=PARAMS.logfile,
                        format='%(asctime)s %(name)-18s %(levelname)-8s %(message)s',
                        level=logging.INFO)
    tag = r'BingImg'
    oss = OSSLoad(PARAMS.cfg)
    meta = metadata()
    nums = PARAMS.num or 1
    imgs = bingimg_gen(0, int(nums))
    for img in imgs:
        logging.info('Proceed on ' + img['date'])
        imgname = img['date'] + '.jpg'

        metas = meta.pack(
            timestamp=img['timestamp'],
            copyright=bytes(img['copyright'], encoding='utf8'),
            describe=bytes(img['describe'], encoding='utf8'))
            
        if oss.exists(tag, imgname):
            logging.info('Image ' + imgname + ' existed. pass')
            continue
        oss.put(tag, imgname, img['img'] + metas)
        logging.info('Image ' + imgname + ' Saved.')
